#include "qtc_alarm_persist/ESSink.h"
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDir>
#include <QDebug>

namespace qtc_alarm_persist {

ESSink::ESSink(const Config& cfg, QObject* parent)
  : QObject(parent), m_cfg(cfg) {
  m_timer.setInterval(m_cfg.flush_interval_ms);
  connect(&m_timer, &QTimer::timeout, this, &ESSink::onFlushTimer);
}

ESSink::~ESSink() { stop(); }

void ESSink::start() {
  if (!m_cfg.log_file.isEmpty()) {
    m_log.setFileName(m_cfg.log_file);
    if (!m_log.open(QIODevice::Append | QIODevice::Text)) {
      emit error(QString("Failed to open log file: %1").arg(m_cfg.log_file));
    }
  }
  m_timer.start();
  emit info("ESSink started");
}

void ESSink::stop() {
  m_timer.stop();
  flush();
  if (m_log.isOpen()) m_log.close();
  emit info("ESSink stopped");
}

void ESSink::append(const qtc_alarm_core::AlarmEvent& ev) {
  m_queue.enqueue(ev);
  if (m_queue.size() >= m_cfg.bulk_size) flush();
}

void ESSink::flush() {
  if (m_queue.isEmpty()) return;
  QByteArray ndjson;
  while (!m_queue.isEmpty()) {
    const auto ev = m_queue.dequeue();
    ndjson.append(toBulkLine(ev));
  }
  if (m_cfg.dry_run) {
    if (m_log.isOpen()) {
      m_log.write("# bulk payload at ");
      m_log.write(QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs).toUtf8());
      m_log.write("\n");
      m_log.write(ndjson);
      m_log.write("\n");
      m_log.flush();
    }
    emit info(QString("Bulk NDJSON logged: %1 bytes").arg(ndjson.size()));
  } else {
    sendBulk(ndjson);
  }
}

void ESSink::onFlushTimer() { flush(); }

QString ESSink::indexNameFor(const qtc_alarm_core::AlarmEvent& ev) const {
  const auto dt = QDateTime::fromMSecsSinceEpoch(ev.time_epoch_ms, Qt::UTC);
  const auto y = dt.date().year();
  const auto m = dt.date().month();
  QString suffix = QString("%1.%2").arg(y).arg(m, 2, 10, QLatin1Char('0'));
  QString type = ev.type.isEmpty() ? QString("state") : ev.type;
  return QString("%1_alarms_%2-%3").arg(m_cfg.index_prefix, type, suffix);
}

QByteArray ESSink::toBulkLine(const qtc_alarm_core::AlarmEvent& ev) const {
  // action/metadata line
  QJsonObject meta;
  meta["index"] = QJsonObject{
    {"_index", indexNameFor(ev)}
  };
  QByteArray out = QJsonDocument(meta).toJson(QJsonDocument::Compact);
  out.append('\n');
  // source line
  auto obj = ev.toJson();
  // Ensure ES date mapping friendly fields
  obj["@timestamp"] = QDateTime::fromMSecsSinceEpoch(ev.time_epoch_ms, Qt::UTC).toString(Qt::ISODateWithMs);
  out.append(QJsonDocument(obj).toJson(QJsonDocument::Compact));
  out.append('\n');
  return out;
}

void ESSink::sendBulk(const QByteArray& ndjson) {
  const QUrl url(QString("%1/_bulk").arg(m_cfg.es_uri));
  QNetworkRequest req(url);
  req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-ndjson");
  auto* reply = m_net.post(req, ndjson);
  connect(reply, &QNetworkReply::finished, this, [this, reply]() {
    if (reply->error() != QNetworkReply::NoError) {
      emit error(QString("Bulk POST failed: %1").arg(reply->errorString()));
    } else {
      emit info("Bulk POST succeeded");
    }
    reply->deleteLater();
  });
}

} // namespace qtc_alarm_persist